using System;
using System.Buffers;
using System.Threading.Tasks;
using eLibrary;
using ePipe;
using eTask.Extensions;
using Letter.IO;
using Letter.IO.Binary;
using Kcptun =  Letter.Net.Kcp.Library.KCP.KCP;

namespace Letter.Net.Kcp;

partial class KcpSession
{
     sealed class KcpSessionRcver : IAsyncReleasable
     {
          public KcpSessionRcver(KcpSession transport, Func<IPipe<byte>> func)
          {
               this.transport = transport;
               this.inputChannel = Pipeline.CreateMultiplePipe<byte>(PipeOptions<byte>.Default);
               this.outputChannel = func.Invoke();
          }
          
          private KcpSession transport;
          private IPipe<byte> inputChannel;
          private IPipe<byte> outputChannel;
          private Task readInputTask;

          private Kcptun Kcp
          {
               get { return this.transport.kcp; }
          }

          private IPipeReader<byte> InputReader
          {
               get { return this.inputChannel.Reader; }
          }

          private IPipeWriter<byte> InputWriter
          {
               get { return this.inputChannel.Writer; }
          }

          public IPipeReader<byte> OutputReader
          {
               get { return this.outputChannel.Reader; }
          }

          private IPipeWriter<byte> OutputWriter
          {
               get { return this.outputChannel.Writer; }
          }
          
          public void Start()
          {
               this.readInputTask = this.ReadInputAsync();
          }

          public void InputUdpMessage(ref ReadOnlySequence<byte> sequence)
          {
               var bytesWriter = this.InputWriter.ToBytesWriter();
               bytesWriter.WriteSequence(ref sequence);
               this.InputWriter.FlushAsync().Ignore();
          }

          private async Task ReadInputAsync()
          {
               while (true)
               {
                    var result = await this.InputReader.ReadAsync();
                    if (result.IsCanceled || result.IsCompleted)
                    {
                         break;
                    }

                    var segment = result.Buffer.First.GetArray();
                    var inputN = this.Kcp.Input(segment.Array, segment.Offset, segment.Count, true, false);
                    if (inputN < 0)
                    {
                         break;
                    }
                    this.InputReader.AdvanceTo(result.Buffer.End);

                    if (this.Kcp.PeekSize() < 1)
                    {
                         continue;
                    }

                    for (;;)
                    {
                         var memory = this.OutputWriter.GetMemory();
                         var segment2 = memory.GetArray();
                         var n = this.Kcp.Recv(segment2.Array, segment2.Offset, segment2.Count);
                         if (n > 0)
                         {
                              this.OutputWriter.Advance(n);
                         }
                         else if (n == 0)
                         {
                              this.OutputWriter.FlushAsync().Ignore();
                              break;
                         }
                    }
               }
               this.InputReader.Complete();
          }


          public async ValueTask ReleaseAsync()
          {
               this.InputReader.Complete();
               this.OutputReader.Complete();
               await this.readInputTask;
          }
     }
}